<?php
/**
 * quick_curd
 * ============================================================================
 * 版权所有 2024-2099 www.wltx666.com，并保留所有权利。
 * 网站地址: https://www.wltx666.com
 * ============================================================================
 * Author: 草上飞
 * Email: wei@alipay16.cn
 * QQ: 2719875001
 * Date: 2025/7/11
 * 描述:
 */

namespace app\result;

class BackupResult extends BaseResult
{
    private $backupPath = '';

    public function __construct()
    {
        parent::__construct();
        $this->backupPath = app()->getRootPath() . 'backup/';
        if (!is_dir($this->backupPath)) {
            @mkdir($this->backupPath, 0755, true);
        }
    }

    public function save_data($args): array
    {
        $backup_type = $args['backup_type'] ?? 'all';//备份类型，all-网站和数据，website-网站，db-数据库
        // 备份路径
        $dir = app()->getRootPath();
        $backupPath = $this->backupPath;
        bs_log('dir:' . $dir, '备份', 'backup.log');
        if (in_array($backup_type, ['all', 'website'])) {
            // 1. 网站文件备份
            $websiteSaveName = date('YmdHi') . '_web.zip';
            if (!class_exists('\ZipArchive')) {
                return $this->error_return('请安装zip扩展');
            }

            $websiteZip = new \ZipArchive();
            if ($websiteZip->open($backupPath . $websiteSaveName, \ZipArchive::CREATE) !== true) {
                return $this->error_return('网站文件压缩失败');
            }

            $while_dirs = [
                $dir . 'backup/',
                $dir . 'runtime/',
            ];
            bs_log('start:' . ($stime = microtime(true)), '网站备份', 'backup.log');

            // 过滤掉open_basedir限制的目录
            $filter = \RecursiveIteratorIterator::SELF_FIRST;
            $iterator = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
                $filter
            );
            
            foreach ($iterator as $file) {
                $pathname = $file->getPathname();
                
                // 检查是否在排除目录中
                $shouldExclude = false;
                foreach ($while_dirs as $excludeDir) {
                    if (strpos($pathname, $excludeDir) === 0 || $pathname === $excludeDir) {
                        $shouldExclude = true;
                        break;
                    }
                }
                
                if ($shouldExclude) {
                    continue;
                }
                
                // 过滤zip文件
                if (str_ends_with($pathname, '.zip')) {
                    continue;
                }
                
                // 检查文件是否在允许的路径内，避免open_basedir限制
                if ($file->isFile()) {
                    $relativePath = str_replace($dir, '', $pathname);
                    $websiteZip->addFile($pathname, $relativePath);
                }
            }
            $websiteZip->close();
            admin_log('备份网站', "网站包: {$websiteSaveName}");
            bs_log('end:' . (microtime(true) - $stime), '网站备份', 'backup.log');
        }

        if (in_array($backup_type, ['all', 'db'])) {
            // 2. 数据库备份
            $dbConfig = config('database');
            bs_log('start:' . ($stime = microtime(true)), '备份数据库', 'backup.log');

            $dbConfig = $dbConfig['connections'][$dbConfig['default']];
            $host = $dbConfig['hostname'];
            $dbname = $dbConfig['database'];
            $user = $dbConfig['username'];
            $password = $dbConfig['password'];
            $port = $dbConfig['hostport'];
            $charset = $dbConfig['charset'];

            try {
                // 使用PDO连接数据库
                $pdo = new \PDO("mysql:host=$host;dbname=$dbname;port=$port;charset=$charset", $user, $password);
                $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

                // 生成SQL文件
                $sqlFilePath = $backupPath . $dbname . '_' . date('YmdHi') . '.sql';
                $sqlFileHandle = fopen($sqlFilePath, 'w');

                // 获取所有表名
                $tables = $pdo->query('SHOW TABLES')->fetchAll(\PDO::FETCH_NUM);
                //bs_log($tables,'tables','backup.log');
                foreach ($tables as $tableRow) {
                    $table = $tableRow[0];

                    // 导出表结构
                    $schema = $pdo->query("SHOW CREATE TABLE `{$table}`")->fetch(\PDO::FETCH_ASSOC);
                    //bs_log($schema,'schema','backup.log');
                    fwrite($sqlFileHandle, "\n\n-- 表结构 `{$table}`\n");
                    fwrite($sqlFileHandle, "DROP TABLE IF EXISTS `{$table}`;\n");
                    fwrite($sqlFileHandle, $schema['Create Table'] . ";\n\n");

                    // 导出表数据
                    $rows = $pdo->query("SELECT * FROM `{$table}`")->fetchAll(\PDO::FETCH_ASSOC);
                    foreach ($rows as $row) {
                        $values = [];
                        foreach ($row as $value) {
                            $values[] = is_null($value) ? 'Null' : $pdo->quote($value);
                        }
                        fwrite($sqlFileHandle, "INSERT INTO `{$table}` (`" . implode('`, `', array_keys($row)) . "`) VALUES (" . implode(', ', $values) . ");\n");
                    }
                }


                // 单独打包SQL文件
                $sqlSaveName = date('YmdHi') . '_db.zip';
                $sqlZip = new \ZipArchive();
                if ($sqlZip->open($backupPath . $sqlSaveName, \ZipArchive::CREATE) !== true) {
                    return $this->error_return('数据库压缩失败');
                }

                $sqlZip->addFile($sqlFilePath, basename($sqlFilePath));
                $sqlZip->close();

                // 删除临时SQL文件
                unlink($sqlFilePath);
                bs_log('end:' . (microtime(true) - $stime), '备份数据库', 'backup.log');
            } catch (\PDOException $e) {
                bs_log($e->getMessage(), 'error-db', 'backup.log');
                return $this->error_return('数据库备份失败：' . $e->getMessage());
            }
            admin_log('备份数据库', "数据库包: {$sqlSaveName}");
        }

        return $this->success_return('备份成功');
    }



    // 表格列
    public function el_table_column(): array
    {
        return [
            ['prop' => 'file_name', 'type' => 'text', 'label' => '文件名称', 'minWidth' => '120', 'align' => 'center',],
            ['prop' => 'size', 'type' => 'text', 'label' => '文件大小', 'minWidth' => '120', 'align' => 'center',],
            ['prop' => 'create_time', 'type' => 'text', 'label' => '创建时间', 'minWidth' => '120', 'align' => 'center',],
            ['prop' => 'update_time', 'type' => 'text', 'label' => '更新时间', 'minWidth' => '120', 'align' => 'center',],
        ];
    }

    //列表搜索字段
    private function _searchField(): array
    {
        return [

        ];
    }

    //列表页面配置
    public function list_config(): array
    {
        return $this->success_return('success', [
            'form_fields' => method_exists($this, 'form_fields') ? $this->form_fields() : [],
            'el_table_column' => method_exists($this, 'el_table_column') ? $this->el_table_column() : [],
            'search_fields' => method_exists($this, '_searchField') ? $this->_searchField() : []
        ]);
    }

    public function list($args = []): array
    {
        $total = 0;
        $data = [];
        $files = scandir($this->backupPath);
        foreach ($files as $file) {
            if (is_file($this->backupPath . $file)) {
                if (!str_contains($file, '.zip')) {
                    continue;
                }
                $total++;
                $data[] = [
                    'id' => $file,
                    'size' => ToolsResult::format_bytes(filesize($this->backupPath . $file)),
                    'file_name' => $file,
                    'create_time' => date('Y-m-d H:i:s', filectime($this->backupPath . $file)),
                    'update_time' => date('Y-m-d H:i:s', filemtime($this->backupPath . $file)),
                ];
            }
        }
        if ($data) {
            $data = array_reverse($data);
        }

        return $this->success_return('success', [
            'total' => $total,
            'list' => $data,
        ]);
    }

    public function del_data($args): array
    {
        $id = $args['id'] ?? '';
        if (!$id) {
            return $this->error_return('请选择要删除的文件！');
        }
        $file = $this->backupPath . $id;
        if (!is_file($file)) {
            return $this->error_return('文件不存在！');
        }
        if (!unlink($file)) {
            return $this->error_return('删除失败！');
        }
        admin_log('删除备份文件', $id);
        return $this->success_return('删除成功');
    }

    public function restore($args): array
    {
        //还原 fixme 实现
        return $this->error_return('开发者未实现此功能！请勿使用！');
    }

    public function download($args)
    {
        $id = $args['id'] ?? '';
        if (!$id) {
            return $this->error_return('请选择要下载的文件！');
        }
        $file = $this->backupPath . $id;
        if (!is_file($file)) {
            return $this->error_return('文件不存在！');
        }
        // 下载文件，浏览器输出
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . basename($file) . '"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file));
        readfile($file);
        //不要return ！！！
        //return $this->success_return('下载成功');
    }

}
